package com.ninong.ker.workflow.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.map.MapUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.microsoft.schemas.vml.CTTextPath;
import com.ninong.ker.admin.domain.FwGroupData;
import com.ninong.ker.admin.rest.vo.HistoricProcessInstanceVO;
import com.ninong.ker.admin.service.FwGroupDataService;
import com.ninong.ker.common.exception.CmdException;
import com.ninong.ker.workflow.conf.RejectTask;
import org.activiti.engine.HistoryService;
import org.activiti.engine.ManagementService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.history.*;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.repository.ProcessDefinition;
import com.google.common.collect.Maps;
import com.ninong.ker.admin.service.RoleService;
import com.ninong.ker.admin.service.TaskService;
import com.ninong.ker.admin.service.dto.ActTaskDTO;
import com.ninong.ker.common.auth.SecurityAdmin;
import com.ninong.ker.common.user.OnlineUser;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Comment;
import org.activiti.engine.task.Task;
import org.activiti.engine.task.TaskQuery;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;
/**
 * @author jmz
 */
@Service
@org.apache.dubbo.config.annotation.Service(version = "1.0.0")
public class TaskServiceImpl implements TaskService {

    /**
     * 已处理
     */
    private static final String TASK_DONE = "DONE";
    @Autowired
    private org.activiti.engine.TaskService taskService;

    @Autowired
    private SecurityAdmin securityAdmin;

    @Autowired
    private RepositoryService repositoryService;
    @Autowired
    private RuntimeService runtimeService;
    @Autowired
    private HistoryService historyService;
    @Autowired
    private ManagementService managementService;

    @Reference(version = "1.0.0", check = false)
    private RoleService roleService;

    @Reference(version = "1.0.0", check = false)
    private FwGroupDataService fwGroupDataService;

    @Override
    public Map<String, Object> listPage(ActTaskDTO dto, String token) {
        Map<String, Object> map = Maps.newHashMap();
        if (StringUtils.equalsAnyIgnoreCase(TASK_DONE, dto.getTaskType())) {
            map = getTaskDoneMap(dto, token);
        } else {
            map = getTaskNeedMap(dto, token);
        }
        return map;
    }

    /**
     * 已办
     *
     * @param dto
     * @param token
     * @return
     */
    private Map<String, Object> getTaskDoneMap(ActTaskDTO dto, String token) {
        List<ActTaskDTO> list = Lists.newArrayList();
        OnlineUser user = securityAdmin.currentUser(token);
        HistoricProcessInstanceQuery taskQuery = historyService.createHistoricProcessInstanceQuery()
                .involvedUser(user.getId())
                .finished();
        long totalItems = taskQuery.count();
        List<HistoricProcessInstance> tasks = taskQuery.listPage((dto.getCurrentPage() - 1) * dto.getPageSize(), dto.getPageSize());
        if (totalItems != 0) {
            List<ActTaskDTO> actTaskDtos = tasks.stream()
                    .map(t -> new ActTaskDTO(t, fwGroupDataService.get(t.getBusinessKey())))
                    .collect(Collectors.toList());
            list.addAll(actTaskDtos);
        }
        Map<String, Object> map = Maps.newHashMap();
        map.put("list", list);
        map.put("total", totalItems);
        return map;
    }

    /**
     * 待办
     *
     * @param dto
     * @param token
     * @return
     */
    private Map<String, Object> getTaskNeedMap(ActTaskDTO dto, String token) {
        List<ActTaskDTO> list = Lists.newArrayList();
        OnlineUser user = securityAdmin.currentUser(token);
        List<String> roles = roleService.listTreeByUserId(user.getId());
        // 使用版本6.x中的接口
        TaskQuery taskQuery = taskService.createTaskQuery();
        taskQuery = taskQuery.or().taskCandidateOrAssigned(user.getId(), roles).taskOwner(user.getId()).endOr().orderByTaskCreateTime().desc();
        List<Task> tasks = taskQuery.listPage((dto.getCurrentPage() - 1) * dto.getPageSize(), dto.getPageSize());
        long totalItems = taskQuery.count();
        if (totalItems != 0) {
            Set<String> processInstanceIdIds = tasks.parallelStream().map(t -> t.getProcessInstanceId()).collect(Collectors.toSet());
            List<ProcessInstance> processInstanceList = runtimeService.createProcessInstanceQuery().processInstanceIds(processInstanceIdIds).list();
            List<ActTaskDTO> actTaskDtos = tasks.stream()
                    .map(t -> new ActTaskDTO(t, processInstanceList.parallelStream().filter(pi -> t.getProcessInstanceId().equals(pi.getId())).findAny().get(), fwGroupDataService.get(t.getBusinessKey())))
                    .collect(Collectors.toList());
            list.addAll(actTaskDtos);
        }
        Map<String, Object> map = Maps.newHashMap();
        map.put("list", list);
        map.put("total", totalItems);
        return map;
    }


    @Override
    public Map<String, Object> insert(String title, FwGroupData entity) {
        String value = entity.getValue();
        Map<String, Object> variables = Maps.newHashMap();
        if (StringUtils.isNotEmpty(value)) {
            JSONObject jsonObject = JSONObject.parseObject(value);
            JSONArray fields = jsonObject.getJSONArray("fields");
            fields.forEach(item -> {
                JSONObject json = JSONObject.parseObject(item.toString());
                variables.put(json.getString("title"), json.getString("value"));
            });
        }
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(title, entity.getId(), variables);
        Map<String, Object> map = Maps.newHashMap();
        map.put("instanceId", processInstance.getId());
        return map;
    }

    @Override
    public void applyTask(Map<String, Object> variables, String token) {
        OnlineUser user = securityAdmin.currentUser(token);
        String taskId = variables.get("taskId").toString();
        // 利用任务对象，获取流程实例id
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        String processInstancesId = task.getProcessInstanceId();
        taskService.addComment(taskId, processInstancesId, variables.get("comment").toString());
        taskService.complete(taskId, variables);
    }

    @Override
    public List<HistoricProcessInstanceVO> listTaskHistorical(String instanceId) {
        List<Comment> list = taskService.getProcessInstanceComments(instanceId);
        List<HistoricProcessInstanceVO> data = list.stream().map(t -> new HistoricProcessInstanceVO(t, historyService.createHistoricTaskInstanceQuery().taskId(t.getTaskId()).singleResult())).collect(Collectors.toList());
        return data;
    }

    @Override
    public void rejectTask(Map<String, Object> variables, String token) {
        OnlineUser user = securityAdmin.currentUser(token);
        String taskId = variables.get("taskId").toString();
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        if (BeanUtil.isEmpty(task)) {
            throw new CmdException("任务不存在");
        }
        managementService.executeCommand(new RejectTask(task.getId(), Maps.newHashMap(), "驳回"));
    }

    @Override
    public void taskRollBack(String taskId) {

    }

    public ProcessDefinition processDefinition(String processDefinitionId) {
        List<ProcessDefinition> list = this.repositoryService.createProcessDefinitionQuery().processDefinitionKey(processDefinitionId).orderByProcessDefinitionVersion().asc().list();
        ProcessDefinition processDefinition;
        if (!list.isEmpty()) {
            processDefinition = list.get(0);
        } else {
            processDefinition = repositoryService.getProcessDefinition(processDefinitionId);
        }
        return processDefinition;
    }
}
